<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
    </head>
    <body>
      https://www.jb51.net/javascript/301560ija.htm
      https://www.jb51.net/javascript/301560ija.htm
      https://www.jb51.net/javascript/301560ija.htm
        <script>

            // 简单对象 拷贝
            // var target = {
            //   a: 1,
            //   b: "bbb",
            //   c: {
            //     k: 1,
            //     k2: 2,
            //   },
            //   d: {
            //     jj: 34,
            //   },
            //   e: {
            //     mm: 78,
            //   },
            // };

            function deepClone(source) {
                if (typeof source !== 'object' || source == null) {
                    return source;
                }
                let target = {};
                for (const key in source) {
                    if (typeof source[key] == 'object' || source[key] != null) {
                        target[key] = deepClone(source[key]);
                    } else {
                        target[key] = source[key];
                    }
                }
                return target;
            }

            function deepClone(source) {
                let clone = {};
                if (typeof source !== 'object' || source == null) {
                    return source;
                }
                let target = Array.isArray(source) ? [] : {};
                for (const key in source) {
                    if (Object.prototype.hasOwnProperty.call(source, key)) {
                        if (typeof source[key] === 'object' && source[key] !== null) {
                            target[key] = deepClone(source[key]);
                        } else {
                            target[key] = source[key];
                        }
                    }
                }
                return target;
            }

            // let res = deepClone(target);
            // console.log(res);
            // target.a = 55; // 不影响拷贝出的对象
            // target.c = 66; // 不影响拷贝出的对象
            // target.d = { nn: 2 }; // 不影响拷贝出的对象

            // target.e.mm = 89; // !!!! 影响拷贝出的对象
            // console.log("target ", target);
            // console.log("res ", res);



            // 比之前的1.0版本多了个存储对象的容器WeakMap，思路就是，初次调用deepCopy时，参数会创建一个WeakMap结构的对象，这种数据结构的特点之一是，存储键值对中的健必须是对象类型。
            // 首次调用时，weakMap为空，不会走上面那个if(hash.has())语句，如果待拷贝对象中有属性也为对象时，则将该待拷贝对象存入weakMap中，此时的健值和健名都是对该待拷贝对象的引用
            // 然后递归调用该函数
            // 再次进入该函数，传入了上一个待拷贝对象的对象属性的引用和存储了上一个待拷贝对象引用的weakMap，因为如果是循环引用产生的闭环，那么这两个引用是指向相同的对象的，因此会进入if(hash.has())语句内，然后return，退出函数，所以不会一直递归进栈，以此防止栈溢出。

            // 利用WeakMap、递归实现对象深拷贝。解决拷贝对象环引用爆栈问题
            function checktype(obj) {
                //检查对象类型
                return Object.prototype.toString.call(obj).slice(8, -1);
            }
            function depCopy(target, hash = new WeakMap()) {
                //hash 作为一个检查器，避免对象深拷贝中出现环引用，导致爆栈。
                let type = checktype(target);
                let result = null;
                if (type == 'Object') {
                    result = {};
                } else if (type == 'Array') {
                    result = [];
                } else {
                    return target;
                }
                if (hash.has(target)) {
                    //检查是有存在相同的对象在之前拷贝过，有则返回之前拷贝后存于hash中的对象
                    return hash.get(target);
                }
                hash.set(target, result); //备份存在hash中，result目前是空对象、数组。后面会对属性进行追加，这里存的值是对象的栈
                for (let i in target) {
                    if (checktype(target[i]) == 'Object' || checktype(target[i]) == 'Array') {
                        result[i] = depCopy(target[i], hash); //属性值是对象，进行递归深拷贝
                    } else {
                        result[i] = target[i]; //其他类型直接拷贝
                    }
                }
                return result;
            }

            var b = { gg: 123, ff: a };
            var a = {
                c: {
                    k: 1,
                    k2: 2,
                },
                d: {
                    d1: [1, 2],
                    d2: b,
                    d3: {
                        f1: 'kkk',
                    },
                },
            };

            let cop = depCopy(a);
        </script>
    </body>
</html>
